package com.zoe.onelink.logging.biz.aspect;

import com.zoe.onelink.logging.biz.annotation.BizLog;
import com.zoe.onelink.logging.biz.context.BizLogContext;
import com.zoe.onelink.logging.biz.entity.BizLogInfo;
import com.zoe.onelink.logging.biz.evaluator.BizLogExpressionEvaluator;
import com.zoe.onelink.logging.biz.evaluator.BizLogRootObject;
import com.zoe.onelink.logging.biz.persistent.BizLogPersistentFactory;
import com.zoe.onelink.logging.biz.persistent.IBizLogPersistent;
import com.zoe.onelink.logging.biz.provider.IBizLogOperatorProvider;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.MethodInvocationException;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.MethodArgumentNotValidException;

import java.lang.reflect.Method;
import java.util.Optional;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-10-11
 */
@Slf4j
@Aspect
public class BizLogAspect {

    private final BizLogExpressionEvaluator expressionEvaluator;

    private final BizLogPersistentFactory bizLogPersistentFactory;

    private final IBizLogOperatorProvider bizLogOperatorProvider;

    public BizLogAspect(BizLogPersistentFactory bizLogPersistentFactory,
                        IBizLogOperatorProvider bizLogOperatorProvider) {
        this.bizLogPersistentFactory = bizLogPersistentFactory;
        this.bizLogOperatorProvider = bizLogOperatorProvider;
        this.expressionEvaluator = new BizLogExpressionEvaluator();
    }


    @Around("@annotation(bizLog)")
    public Object aspect(ProceedingJoinPoint point, BizLog bizLog) throws Throwable {

        Object[] args = point.getArgs();
        Signature signature = point.getSignature();
        Object target = point.getTarget();
        Method method = ((MethodSignature) signature).getMethod();
        MethodInvokeResult methodInvokeResult = new MethodInvokeResult();
        try {
            // 执行方法
            Object result = point.proceed();
            // 设置方法执行返回结果
            methodInvokeResult.setResult(result);
        } catch (Exception e) {
            // 记录异常
            methodInvokeResult.setThrowable(e);
        }
        try {
            Throwable throwable = methodInvokeResult.getThrowable();
            String errorMsg = throwable == null ? null : throwable.getMessage();
            BizLogRootObject bizLogRootObject = new BizLogRootObject(method, args, methodInvokeResult.getResult(), throwable == null, errorMsg, point.getClass());
            // 创建SpEl表达式上下文
            EvaluationContext evaluationContext = this.expressionEvaluator.createEvaluationContext(bizLogRootObject);
            String expressionTemplate = throwable == null ? bizLog.successMsg() : bizLog.failureMsg();
            AnnotatedElementKey annotatedElementKey = new AnnotatedElementKey(method, target.getClass());
            // 是否可以进行日志记录
            if (!this.doCondition(bizLog.condition(), annotatedElementKey, evaluationContext)) {
                return methodInvokeResult.getResult();
            }
            // 解析日志文本
            String logMsg = StringUtils.isEmpty(expressionTemplate) ? null : this.expressionEvaluator.parseExpression(expressionTemplate, annotatedElementKey, evaluationContext);
            String logDetail = StringUtils.isEmpty(bizLog.detail()) ? null : this.expressionEvaluator.parseExpression(bizLog.detail(), annotatedElementKey, evaluationContext);
            String operatorCode = this.bizLogOperatorProvider.getOperatorCode();
            String operatorName = this.bizLogOperatorProvider.getOperatorName();
            // 构建日志信息
            BizLogInfo bizLogInfo = new BizLogInfo()
                    .setMessage(logMsg)
                    .setDetail(logDetail)
                    .setTag(bizLog.tag())
                    .setSuccess(throwable == null)
                    .setOperatorCode(operatorCode)
                    .setOperatorName(operatorName)
                    .setBizNo(bizLog.bizNo());

            // 获取持久化实例
            IBizLogPersistent persistent = this.bizLogPersistentFactory.getInstance(bizLog.persistent().getSimpleName());
            if (persistent != null) {
                // 持久化日志
                persistent.persist(bizLogInfo, methodInvokeResult);
            }
        } catch (Exception e) {
            log.error("BizLog Error: {}", e.getMessage());
        }
        if (methodInvokeResult.getThrowable() != null) {
            throw methodInvokeResult.getThrowable();
        }
        return methodInvokeResult.getResult();
    }


    /**
     * 执行条件判断
     */
    private boolean doCondition(String condition, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
        if (StringUtils.isEmpty(condition)) {
            return true;
        }
        String result = this.expressionEvaluator.parseExpression(condition, methodKey, evalContext);
        return "true".equals(result);
    }

}
